package 逆变点与协变点

/**
  *
  * Created by juntzhan on 2015/8/3.
  */


object MyVariancesTest extends App {

  class Person

  class Student extends Person


  //========================================================逆变点======================================================
  /**
    * Error:(10, 13) covariant type A occurs in contravariant position in type A of value x
    * def fun(x: A) {}
    */
  //  class In[+A] {
  //    def fun(x: A) {}
  //  }

  //正是因为需要符合里氏替换法则，方法中的参数类型声明时必须符合逆变（或不变），
  // 以让子类方法可以接收更大的范围的参数(处理能力增强)；而不能声明为协变，
  // 子类方法可接收的范围是父类中参数类型的子集(处理能力减弱)。
  // A => Unit 与 Function1[-A, Unit]等价，而
  class In1[-T] {
    def fun(x: T): Unit = {
      //方法参数的位置称为做逆变点(contravariant position)
      println("fun")
    }
  }

  // 子类           父类
  //Student     <: Person
  //In1[Person] <: In1[Student]
  val in1: In1[Student] = new In1[Person]
  in1.fun(new Student())


//    val students = new Array[Student](10)
//    val persons: Array[Person] = students //error
//    persons(0) = new Person//o no students(0) is not a student

  val persons = new Array[Person](10)
  persons(0) = new Student
  persons(1) = new Person

  //========================================================协变点======================================================

  //则与 Function0[+A] 等价
  class In2[+T] {
    def fun(): T = null.asInstanceOf[T]
  }

  //返回的实例更细
  // 子类           父类
  //Student <: Person
  //In2[Student] <: In2[Person]
  val in2: In2[Person] = new In2[Student]
  in2.fun()

  /**
    * contravariant type A occurs in covariant position in type ()A of method fun
    * def fun(): A = null.asInstanceOf[A]
    */
  //  class In3[-A] {
  //    def fun(): A = null.asInstanceOf[A]
  //  }

//  class Pair[+T](val first: T, val second: T) {
//    def replaceFirst()
//  }

}

object OfficialVariancesTest extends App {

  class Stack[+A] {
    //B出现逆变点  B>:A
    //Stack[B] <: Stack[A]
    //这里B是不可变的所以出现逆变点没有问题
    def push[B >: A](elem: B): Stack[B] = new Stack[B] {
      override def top: B = elem

      override def pop: Stack[B] = Stack.this

      override def toString = elem.toString + " " +
        Stack.this.toString()
    }

    def top: A = sys.error("no element on stack")

    def pop: Stack[A] = sys.error("no element on stack")

    override def toString = ""
  }

  var s: Stack[Any] = new Stack[Any]()
  s = s.push("hello")
  s = s.push(new Object())
  s = s.push(7)
  println(s)
}
